#ifndef _MATH_ANGLE_H_
#define _MATH_ANGLE_H_

#include "./math_define.h"

//namespace Mathematics {
//	//! namespace for angles
//	/*!
//		Angle conversions
//	*/
//	namespace Angler{
//
//		/*! degree unit's upper bound. */
//		const double max_degree = 360.0;
//		/*! radian unit's upper bound. */
//		const double max_radian = 2*pi;
//		/*! grade unit's upper bound. */
//		const double max_grade = 400.0;
//
//		//! Angle conversion.
//		/*! 
//			Conversion from angles, using any linear unit.
//			
//			\param value angle desired to convert
//			\param source_range max angle in the source unit (before angle repetition)
//			\param target_range max angle in the target unit (before angle repetition)
//			\return angle in the new unit (not bounded to range)
//			
//			\code
//			cout << UnitConversion(90,max_degree,max_radian) << endl		// pi/2.0 (in double format)
//			cout << UnitConversion(400,max_degree,max_radian) << endl	// 7*pi/3.0 (in double format)
//			\endcode
//		*/
//		template <class T>
//			inline double UnitConversion(const T &value, const T &source_range, const T &target_range){
//				return (value*target_range)/source_range;
//			};
//			
//		//! Conversion form degree to radian unit.
//		/*! 
//			Conversion from angles.
//			
//			\param degree angle desired to convert
//			\return angle in radian (not bounded to range)
//			
//			\code
//			cout << DtoR(90) << endl	// pi/2.0 (in double format)
//			cout << DtoR(400) << endl	// 7*pi/3.0 (in double format)
//			\endcode
//		*/
//		template <class T>
//			inline double DtoR(const T &degree){
//				return UnitConversion(degree, max_degree, max_radian);
//			};
//		
//		//! Conversion form radian to degree unit.
//		/*! 
//			Conversion from angles.
//			
//			\param radian angle desired to convert
//			\return angle in degree (not bounded to range)
//			
//			\code
//			cout << RtoD( DtoR(90) ) << endl		// 90.0
//			cout << RtoD( DtoR(400) ) << endl	// 400.0
//			\endcode
//		*/
//		template <class T>
//			inline double RtoD(const T &radian){
//				return UnitConversion(radian, max_radian, max_degree);
//			};
//		
//		//! Conversion form degree to grade unit.
//		/*! 
//			Conversion from angles.
//			
//			\param degree angle desired to convert
//			\return angle in grade (not bounded to range)
//			
//			\code
//			cout << DtoG(90) << endl	// 100.0
//			cout << DtoG(360) << endl	// 400.0
//			\endcode
//		*/
//		template <class T>
//			inline double DtoG(const T &degree){
//				return UnitConversion(degree, max_degree, max_grade);
//			};
//			
//		//! Conversion form grade to degree unit.
//		/*! 
//			Conversion from angles.
//			
//			\param grade angle desired to convert
//			\return angle in degree (not bounded to range)
//			
//			\code
//			cout << GtoD(-100) << endl	// -90.0
//			cout << GtoD(400) << endl	// 360.0
//			\endcode
//		*/
//		template <class T>
//			inline double GtoD(const T &grade){
//				return UnitConversion(grade, max_grade, max_degree);
//			};
//		
//		//! Conversion form grade to radian unit.
//		/*! 
//			Conversion from angles.
//			
//			\param grade angle desired to convert
//			\return angle in radian (not bounded to range)
//			
//			\code
//			cout << GtoR(100) << endl	// pi/2.0 (in double format)
//			cout << GtoR(400) << endl	// 7*pi/3.0 (in double format)
//			\endcode
//		*/
//		template <class T>
//			inline double GtoR(const T &grade){
//				return UnitConversion(grade, max_grade, max_radian);
//			};
//		
//		//! Conversion form radian to grade unit.
//		/*! 
//			Conversion from angles.
//			
//			\param radian angle desired to convert
//			\return angle in grade (not bounded to range)
//			
//			\code
//			cout << RtoG( GtoR(100) ) << endl	// 100.0
//			cout << RtoG( GtoR(500) ) << endl	// 500.0
//			\endcode
//		*/
//		template <class T>
//			inline double RtoG(const T &radian){
//				return UnitConversion(radian, max_radian, max_grade);
//			};
//
//		double ValidAngle(const double &angle, const double ang_max);
//		
//		//! Range bounded degree angle.
//		/*! 
//			Number in the range [0,360[.
//			
//			\param angle value desired to bound
//			\return angle in bounds
//			
//			\code
//			cout << ValidAngleDegree( 450 ) << endl	// 90.0
//			cout << ValidAngleDegree( -450 ) << endl	// 270.0 (= -90.0)
//			\endcode
//		*/
//		inline double ValidAngleDegree(const double &angle){
//			return ValidAngle(angle,max_degree);
//		};
//		
//		//! Range bounded radian angle.
//		/*! 
//			Number in the range [0,2pi[.
//			
//			\param angle value desired to bound
//			\return angle in bounds
//			
//			\code
//			cout << ValidAngleRadian( 5*pi/2 ) << endl	// pi/2.0 (double format)
//			cout << ValidAngleRadian( -5*pi/2 ) << endl	// 3*pi/2.0 (= -pi/2.0)(double format)
//			\endcode
//		*/
//		inline double ValidAngleRadian(const double &angle){
//			return ValidAngle(angle,max_radian);
//		};
//		
//		//! Range bounded grade angle.
//		/*! 
//			Number in the range [0,400[.
//			
//			\param angle value desired to bound
//			\return angle in bounds
//			
//			\code
//			cout << ValidAngleGrade( 450 ) << endl	// 50.0
//			cout << ValidAngleGrade( -450 ) << endl	// 350.0 (= -50.0)
//			\endcode
//		*/
//		inline double ValidAngleGrade(const double &angle){
//			return ValidAngle(angle,max_grade);
//		};
//		
//		extern double *r_sin_list;
//		extern const usint r_sin_list_size;
//		
//		void CreateSinList();
//		void DestroySinList();
//		
//		inline double RSin(const double &degree){
//			if( r_sin_list == NULL )
//				CreateSinList();
//			usint index = Low( ValidAngleDegree( degree ) );
//			return ( index < 180 ?
//				( index < 90 ? r_sin_list[ index ] : r_sin_list[ 180 - index ] ) :
//				- ( index < 270 ? r_sin_list[ index - 180 ] : r_sin_list[ 360 - index ] )
//			);
////			return sin(DtoR(degree));
//		};
//		inline double RCos(const double &degree){
//			return RSin( degree + (max_degree/4.0) );
////			return cos(DtoR(degree));
//		};
//
//		inline double RTan(const double &degree){
//			return RSin( degree ) / RCos( degree );
//		};
//		
//		//! function for testing angle definitions
//		void AngleTest();
//		
//	};
//};

namespace Mathematics {

	class Radian: public Wtb, public Clsb {
	protected:
		double value;

	public:
		static const double max;

		Radian( const double &value = 0.0 ){
			this->value = value;
		}
		Radian( const Radian &angle ){
			this->value = angle.value;
		}
		virtual ~Radian(){};

		inline Radian& operator = ( const double &angle ) {
			this->value = Radian( angle ).value;
			return (*this);
		};
		inline friend Radian& operator += ( Radian &l_angle, const double &r_angle ) {
			l_angle.value += r_angle;
			return l_angle;
		};
		inline friend Radian operator + ( const Radian &l_angle, const double &r_angle ) {
			Radian result( l_angle );
			return result += r_angle;
		};
		inline friend Radian& operator -= ( Radian &l_angle, const double &r_angle ) {
			l_angle.value -= r_angle;
			return l_angle;
		};
		inline friend Radian operator - ( const Radian &l_angle, const double &r_angle ) {
			Radian result( l_angle );
			return result -= r_angle;
		};
		inline friend Radian& operator *= ( Radian &l_angle, const double &value ) {
			l_angle.value *= value;
			return l_angle;
		};
		inline friend Radian operator * ( const Radian &angle, const double &scalar ) {
			Radian result( angle );
			return result *= scalar;
		};
		inline friend Radian operator * ( const double &scalar, const Radian &angle ) {
			return angle * scalar;
		};
		inline friend Radian& operator /= ( Radian &l_angle, const double &value ) {
			if( value == 0.0 )
				exit(-10); // TODO - throw an exception
			l_angle.value /= value;
			return l_angle;
		};
		inline friend Radian operator / ( const Radian &angle, const double &scalar ) {
			Radian result( angle );
			return result /= scalar;
		};

		//inline friend bool operator== ( const Radian &ang1, const Radian &ang2 ) {
		//	return ang1.valid() == ang2.valid();
		//};
		//inline friend bool operator!= ( const Radian &ang1, const Radian &ang2 ) {
		//	return !( ang1 == ang2 );
		//};

		inline operator double () const {
			return this->value;
		};

		/*! [ 0, 2*pi ] */
		inline double valid() const {
			return Fract( 1.0 + Fract( value / max ) ) * max;
		};
		inline double loops() const {
			return value / max;
		};

		inline double sin() const {
			double angle = this->valid();
			return std::sin( angle );
		};
		inline double cos() const {
			double angle = this->valid();
			return std::cos( angle );
		};
		inline double tan() const {
			double angle = this->valid();
			if( angle == pi || angle == 3.0*pi/2.0 )
				exit(-10); // TODO - launch an exception
			return std::tan( angle );
		};

		// --- Writeable ---
		inline virtual string toS() const {
			return dtos( value ) + " radians.";
		};
		virtual bool filePrint( FILE* parq ) const {
			return false;
		};

		// --- Classificable ---
		static inline string ClassName() {
			return "Radian";
		};
		virtual inline string className() const {
			return ClassName();
		};

	};

	class Degree : public Wtb, public Clsb {
	protected:
		double value;
		static const double max;

	public:
		Degree( const double &value = 0.0 ){
			this->value = value;
		}
		Degree( const Radian &angle ){
			this->value = ( (double) angle ) / Radian::max * max;
		}
		virtual ~Degree(){};

		inline operator Radian () const {
			return Radian( value / max * Radian::max );
		};

		inline friend bool operator== ( const Degree &ang1, const Degree &ang2 ) {
			return ang1.value == ang2.value;
		};
		inline friend bool operator!= ( const Degree &ang1, const Degree &ang2 ) {
			return !( ang1 == ang2 );
		};

		// --- Writeable ---
		inline virtual string toS() const {
			return dtos( value ) + " degrees.";
		};
		virtual bool filePrint( FILE* parq ) const {
			return false;
		};

		// --- Classificable ---
		static inline string ClassName() {
			return "Degree";
		};
		virtual inline string className() const {
			return ClassName();
		};

	};

	class Grade : public Wtb, public Clsb {
	protected:
		double value;
		static const double max;

	public:
		Grade( const double &value = 0.0 ){
			this->value = value;
		}
		Grade( const Radian &angle ){
			this->value = ( (double) angle ) / Radian::max * max;
		}
		virtual ~Grade(){};

		inline operator Radian () const {
			return Radian( value / max * Radian::max );
		};

		inline friend bool operator== ( const Grade &ang1, const Grade &ang2 ) {
			return ang1.value == ang2.value;
		};
		inline friend bool operator!= ( const Grade &ang1, const Grade &ang2 ) {
			return !( ang1 == ang2 );
		};

		// --- Writeable ---
		inline virtual string toS() const {
			return dtos( value ) + " grades.";
		};
		virtual bool filePrint( FILE* parq ) const {
			return false;
		};

		// --- Classificable ---
		static inline string ClassName() {
			return "Grade";
		};
		virtual inline string className() const {
			return ClassName();
		};

	};

};

#endif
